7 数组的扩展(ECMAScript6入门)

7.1 Array.from()

功能:可以将两类对象转为真正的数组,从而使用数组丰富的方法

  • 类似数组的对象(array-like object):即存在 length 属性的对象
  • 可遍历(iterable)的对象,包括 ES6 新增的数据结构 Set 和 Map

注意: 类数组对象也可以通过 callapply的方式强行调用Array 数组的方法

参数 说明
参数1 可以被转为数组的对象
参数2 类似数组的 map 方法
参数3 要绑定的上下文

7.1.1 转换类数组对象中为数组

  • DOM 操作返回的 NodeList 集合
  • 函数内部的 arguments 对象
  • 自定义的类数组对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 1. 自定义的类数组对象 */
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

/* 2. DOM 操作返回的 NodeList 集合 */
// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
console.log(p);
});

/*3. 函数内部的 Arguments 对象*/
// arguments对象
function foo() {
var args = Array.from(arguments);
// ...
}

7.1.2 es5环境下的 polyfill

1
2
3
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)()

7.1.3 第二个参数

说明:租用类似于数组的 map方法,用来对每个元素进行处理,将处理后的值放在返回的数组中

7.1.3.1 基础

1
2
3
4
5
6
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);

Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]

7.1.3.2 几个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 1. 取出一组DOM节点的文本内容 */
let spans = document.querySelectorAll('span.name');

// es5的方式:map()
let names1 = Array.prototype.map.call(spans, s => s.textContent);

// es6的方式:Array.from()
let names2 = Array.from(spans, s => s.textContent)

/* 2. 将数组中不饿日志为 false 的成员转为 0 */
Array.from([1, ,2, ,3], (n) => n --|| 0)---
// [1, 0, 2, 0, 3]

/* 3. 返回各种数据的类型 */
function typeof () {
return Array.from(arguments, value => typeof value)
}
typedsOf(null, [], NaN)
// ['object', 'object', 'number']

7.1.3.3 扩展运算符

说明:扩展运算符也可以将可遍历对象(即部署了便利器接口Symbol.iterator)转换为数组,但不支持类数组对象。

1
2
3
4
5
6
7
// arguments对象
function foo() {
var args = [...arguments];
}

// NodeList对象
[...document.querySelectorAll('div')]

7.1.3.4 奇技淫巧

  • 利用第一个参数的 length 属性和第二个参数
1
2
Array.from({ length: 2 }, () => 'jack')
// ['jack', 'jack']
  • 返回字符串的长度(能正确处理各种Unicode字符, 避免 JS 将大于\uFFFF的Unicode的字符算作两个字符的 bug)
1
2
3
function countSymbols(string) {
return Array.from(string).length;
}

7.2 Array.of()

功能:将一组值转换为数组
说明:Array.of基本可以用来替代Array()new Array(),并且不存在由于参数不同而导致的重载

7.2.1 Array.of() vs. Array()

1
2
3
4
5
6
7
8
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

7.2.2 polyfill

1
2
3
function ArrayOf(){
return [].slice.call(arguments);
}

7.3 数组实例的 copyWithin()

Array 实例方法 copyWithin()
功能:将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组
注意:参数都为数值,如果不是,会自动转为数值

Alt text

参数 说明 是否必需 备注
target 目的地 起点下标
start 源 起点下标 默认为0,如果为负值,表示倒数
end 源 终点下标 + 1 默认为0,如果为负数,表示倒数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]

// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}

// 将2号位到数组结束,复制到0号位
var i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]

// 对于没有部署TypedArray的copyWithin方法的平台
// 需要采用下面的写法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]

7.4 数组实例的 find() 和 findIndex()

注意:这两个方法都能够发现NaN,ni

7.4.1 find()

功能:找到第一个符合条件的数组成员
返回值:第一个返回值为true的成员 ,没有则返回 undefined

参数 说明
参数1(必需) 一个回调函数,所有数组成员会依次执行该回调
参数2(可选) 回调函数执行的上下文

回调函数(第一个参数)

参数 说明
value 当前成员值
index 当前成员下标
arr 原数组
1
2
3
4
5
[1, 3, 8, 7, -5].find((n) => n < 0)//-5

[2, 4, 6, 8, 10, 12].find(fcuntion (value, index, arr) {
return value > 9
})// 10

7.4.1 findIndex()

功能:找到第一个符合条件的数组成员的位置
返回值:找到则返回成员位置,否则返回 -1
注意:除了返回值是成员的位置而不是成员本身外,其它方面和 find()完全一致

1
2
3
4
5
[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0

7.5 数组实例的 fill()

功能:使用给定值填充数组,数组中已有的元素会被全部抹去
是否有副作用:
返回值:新数组

参数 说明
参数1(必需) 填充数组的元素
参数2(可选) 填充的开始位置
参数3(可选) 填充的结束位置
1
2
3
4
5
['a', 'b', 'c'].fill(7)// [7, 7, 7]

new Array(3).fill(7)// [7, 7, 7 ]

['a', 'b', 'c'].fill(7, 1, 2)// ['a', 7, 'c']

7.6 数组实例的 entries(), keys()和values()

用途:都针对特定方面遍历数组
返回值:便利器对象,可以通过for-of循环便利器对象的 next 方法进行遍历

方法 说明
keys() 返回数组中key的遍历器对象
values() 返回数组中value的遍历器对象
entries() 返回数组中key-value的遍历器对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
1
2
3
4
5
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
entries.next().value; // [0, 'a']
entries.next().value; // [1, 'b']
entries.next().value; // [2, 'c']

7.7 数组实例的 includes()

功能:判断数组中是否包含给定的值
返回值:true 存在, false 不存在
支持:该方法属于ES7,但 Babel转码器已经支持

参数 说明
参数1(必需) 要搜索的 value
参数2(可选) 搜索的开始位置
1
2
3
[1, 2, 3].includes(2);     // true
[1, 2, 3].includes(4); // false
[1, 2, NaN].includes(NaN); // true

7.7.1 includes() vs. indexOf()

如果用indexOf()替代includes()来确定数组中是否包含给定的值,存在两个问题

  1. 不够语意化,而且还要比较下标是否不等于-1
  2. indexOf()内部使用===进行判断,会导致对NaN的误判
1
2
3
4
5
[NaN].indexOf(NaN)
// -1

[NaN].includes(NaN)
// true

7.7.2 polyfill

使用Array.prototype.some实现includes功能

1
2
3
4
5
6
7
const contains = (() =>
Array.prototype.includes
? (arr, vaue) => arr.includes(value)
: (arr, value) => arr.some(el => el === value)
)()

contrains(['foo', 'bar'], 'baz')// false

7.7.3 Map 和 Set

说明:MapSet数据结构有一个has方法,需要注意和includes区分

Map 结构的has方法:用来查找key

  • Map.prototype.has(key)
  • WeakMap.prototype.has(key)
  • Reflect.has(target, propertyKey)

Set 结构的has方法:用来查找值

  • Set.prototype.has(value)
  • WeakSet.prototype.has(value)

7.8 数组的空位

说明:数组中没有任何值的元素就是一个数组的空位
注意:空位不是undefined

1
2
3
4
Array(3) // [, , ,]

0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false

7.8.1 ES5对空位的处理

数组方法 空位的处理
forEach() 跳过空位
filter() 跳过空位
every() 跳过空位
some() 跳过空位
map() 跳过空位,但返回的数组中仍保留着这个空位
join() 将空位视为undefinedundefined、null会被处理成空字符串)
toString() 将空位视为undefinedundefined、null会被处理成空字符串)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1

// filter方法
['a',,'b'].filter(x => true) // ['a','b']

// every方法
[,'a'].every(x => x==='a') // true

// some方法
[,'a'].some(x => x !== 'a') // false

// map方法
[,'a'].map(x => 1) // [,1]

// join方法
[,'a',undefined,null].join('#') // "#a##"

// toString方法
[,'a',undefined,null].toString() // ",a,,"

7.8.2 ES6对空位的处理

注意:由于空位处理规则非常不统一,所以建议避免出现空位

运算 说明
Array.from() 不会忽略空位,而是将空位转为undefined
扩展运算符 不会忽略空位,而是将空位转为undefined
copyWithin() 连空位一起拷贝
fill() 将空位视为正常的数组位置
for-of 会遍历空位
entries()、keys()、values()、find()、findIndex() 会将空位处理成undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Array.from(['a',,'b'])
// [ "a", undefined, "b" ]

[...['a',,'b']]
// [ "a", undefined, "b" ]

[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]

new Array(3).fill('a') // ["a","a","a"]

let arr = [, ,];
for (let i of arr) {
console.log(1);
}
// 1
// 1

// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]

// keys()
[...[,'a'].keys()] // [0,1]

// values()
[...[,'a'].values()] // [undefined,"a"]

// find()
[,'a'].find(x => true) // undefined

// findIndex()
[,'a'].findIndex(x => true) // 0